﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace QbservableCore.Serialization
{
    internal static class TranslationHelper
    {
        #region AddParamsToJson

        internal static void AddParamsToJson(this JContainer json, IEnumerable<Expression> args
            , ExpressionToJson state)
        {
            var parmItems = from expParameters in args
                            select state.CreateAndTransform(expParameters);
            var parmArray = parmItems.ToArray();
            if (parmArray.Length != 0)
            {
                JArray parms = new JArray(parmArray);
                AddPropertyRaw(json, Consts.Json.Params, parms, state);
            }
        }

        #endregion // AddParamsToJson

        #region AppendJsonToCurrentAsArray

        internal static JContainer AppendJsonToCurrentAsArray(ExpressionToJson state
            , bool append = false)
        {
            JContainer json;
            JArray arr = state.JsonCurrent as JArray;
            if (arr != null)
            {
                json = new JObject();
            }
            else
            {
                json = state.JsonCurrent;
                arr = new JArray();
                state.JsonCurrent = arr;
            }
            if (append)
                arr.Add(json);
            else
                arr.AddFirst(json);
            return json;
        }
 
        #endregion // AppendJsonToCurrentAsArray

        #region ExtractProperty

        /// <summary>
        /// Extracts the property's value by convention.
        /// </summary>
        /// <param name="json">The json.</param>
        /// <param name="key">The key.</param>
        /// <param name="convention">The convention.</param>
        /// <returns>
        /// return null if not exists
        /// </returns>
        public static string ExtractProperty(this JObject json, string key, IQbservableConvention convention)
        {
            string conventionName = convention.FromKeyToConvention(key);
            string value = (string)json[conventionName];
            return value;
        }

        #endregion // ExtractProperty

        #region ExtractPropertyRaw

        /// <summary>
        /// Extracts the property's value by convention.
        /// </summary>
        /// <param name="json">The json.</param>
        /// <param name="key">The key.</param>
        /// <param name="convention">The convention.</param>
        /// <returns>
        /// return null if not exists
        /// </returns>
        public static T ExtractPropertyRaw<T>(this JObject json, string key, 
            IQbservableConvention convention) 
            where T : JToken
        {
            string conventionName = convention.FromKeyToConvention(key);
            JToken value = json[conventionName];
            return (T)value;
        }

        #endregion // ExtractPropertyRaw

        #region ExtractPropertyToKey

        /// <summary>
        /// Extracts the property's value by convention and convert the value to the original key.
        /// </summary>
        /// <param name="json">The json.</param>
        /// <param name="key">The key.</param>
        /// <param name="convention">The convention.</param>
        /// <returns>
        /// return null if not exists
        /// </returns>
        public static string ExtractPropertyToKey(this JObject json, string key, IQbservableConvention convention)
        {
            string conventionName = convention.FromKeyToConvention(key);
            string value = (string)json[conventionName];
            value = convention.FromConventionToKey(value);
            return value;
        }

        #endregion // ExtractPropertyToKey

        #region ExtractPropertyType

        /// <summary>
        /// Extracts the property's type by convension.
        /// </summary>
        /// <param name="json">The json.</param>
        /// <param name="convention">The convention.</param>
        /// <returns>
        /// return null if not exists
        /// </returns>
        public static Type ExtractPropertyType(
            this JObject json, 
            IQbservableConvention convention)
        {
            string conventionName = convention.FromKeyToConvention(Consts.Json.Type);
            string typeValue = (string)json[conventionName];
            Type t = convention.FromConventionToType(typeValue);
            return t;
        }

        #endregion // ExtractPropertyType

        #region AddProperty

        /// <summary>
        /// Adds the property into json container.
        /// </summary>
        /// <param name="json">The json.</param>
        /// <param name="name">The name.</param>
        /// <param name="expType">Type of the exp.</param>
        /// <param name="state">The state.</param>
        public static void AddProperty(
            this JContainer json,
            string name,
            ExpressionType expType,
            ExpressionToJson state)
        {
            string value = state.Convention.FromExpressionTypeToConvention(expType);
            string propName = state.Convention.FromKeyToConvention(name);
            AddProperty(json, propName, value);
        }

        /// <summary>
        /// Adds the property into json container.
        /// </summary>
        /// <param name="json">The json.</param>
        /// <param name="name">The name.</param>
        /// <param name="value"></param>
        /// <param name="state">The state.</param>
        public static void AddProperty(
            this JContainer json,
            string name,
            string value,
            ExpressionToJson state)
        {
            value = state.Convention.FromKeyToConvention(value);
            string propName = state.Convention.FromKeyToConvention(name);
            AddProperty(json, propName, value);
        }

        /// <summary>
        /// Adds the property into json container.
        /// </summary>
        /// <param name="json">The json.</param>
        /// <param name="name">The name.</param>
        /// <param name="value"></param>
        /// <param name="state">The state.</param>
        public static void AddPropertyRaw(
            this JContainer json,
            string name,
            object value,
            ExpressionToJson state)
        {
            string propName = state.Convention.FromKeyToConvention(name);
            AddProperty(json, propName, value);
        }

        /// <summary>
        /// Adds the property into json container.
        /// </summary>
        /// <param name="json">The json.</param>
        /// <param name="type"></param>
        /// <param name="state">The state.</param>
        public static void AddProperty(
            this JContainer json,
            Type type,
            ExpressionToJson state,
            string name = Consts.Json.Type)
        {
            string value = state.Convention.FromTypeToConvention(type);
            string propName = state.Convention.FromKeyToConvention(name);
            AddProperty(json, propName, value);
        }

        private static void AddProperty(JContainer json, string propName, object value)
        {
            var jObj = json as JObject;
            if (jObj == null)
            {
                if (json is JArray)
                {
                    jObj = new JObject();
                    json.Add(jObj);
                }
                else
                    throw new NotImplementedException();
            }

            var p = new JProperty(propName, value);
            jObj.Add(p);
        }

        #endregion // AddProperty
   }
}
